#include "pch.hpp"
#include "CameraComponent.hpp"

using namespace Engine;
using namespace Engine::Entity;




CameraComponent::CameraComponent(Input::Device::ptr inputDevice, Video::Device::ptr videoDevice)
	: Component(false)

	, m_inputDevice(inputDevice)
	, m_camera(videoDevice->camera())
	, m_lookAt(nullptr)

	, m_latitude(units::angle<float>::degrees(0))
	, m_longitude(units::angle<float>::degrees(0))
	, m_radius(units::length<float>::meters(1))
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

CameraComponent::~CameraComponent()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




Component::ID CameraComponent::id() const
{
	return Components::Camera;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Component::IDs CameraComponent::requiredComponents() const
{
	IDs components;
	components.push_back(Components::Position);
	return components;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void CameraComponent::attached(Entity::Object* entity, const map& components)
{
	__super::attached(entity, components);

	// We only require this component
	auto i = components.find(Components::Position);
	verify(i != components.end())
	m_lookAt = i->second;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void CameraComponent::detached(Entity::Object* entity)
{
	__super::detached(entity);

	m_lookAt = Component::ptr(nullptr);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void CameraComponent::update()
{
	if(!m_lookAt)
		return;

	m_targetCameraPosition.x = m_radius.meters() * m_latitude.sin() * m_longitude.cos();
	m_targetCameraPosition.y = m_radius.meters() * m_latitude.cos();
	m_targetCameraPosition.z = m_radius.meters() * m_latitude.sin() * m_longitude.sin();

	// TODO: Use an interpolator
	m_cameraPosition = m_targetCameraPosition;
	m_cameraLookAt   = m_lookAt->position();

	m_camera->setPosition(m_cameraPosition);
	m_camera->setLookAt(m_cameraLookAt);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void CameraComponent::registerType(Script::System::ptr engine)
{
	engine->registerObjectType("CameraComponent", 0, asOBJ_REF);
	engine->registerObjectBehaviour("CameraComponent", asBEHAVE_ADDREF, "void f()", asMETHOD(Script::Object, AddRef), asCALL_THISCALL);
	engine->registerObjectBehaviour("CameraComponent", asBEHAVE_RELEASE, "void f()", asMETHOD(Script::Object, Release), asCALL_THISCALL);

	engine->registerObjectBehaviour("CameraComponent", asBEHAVE_IMPLICIT_REF_CAST, "Component@ f()", asFUNCTION((&refCast<CameraComponent, Component>)), asCALL_CDECL_OBJLAST);
	engine->registerObjectBehaviour("Component", asBEHAVE_REF_CAST, "CameraComponent@ f()", asFUNCTION((&refCast<Component, CameraComponent>)), asCALL_CDECL_OBJLAST);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
